<?php
/* {
Citru - www.citru.se
See license.txt for copyright and license information.
} */

if(!defined("_CITRU")) {die('This file should not be accessed directly.');}
require_once 'Module.php';

class UserModule extends Module {
	
	protected $moduleTabs = array("users", "edit", "add", "permissions");
	protected $moduleName = "UserModule";
	protected $instanceName;
	protected $dbModule;
	protected $permissions;
	
	/**
	 * Creates a new user module object
	 *
	 * @param string $instance 				name of the instance
	 * @param string $db_module 			database module to load data from
	 * @param arrau $permissions 			permissions to views
	 * @return void
	 */function __construct($instance, $db_module, $permissions) {
		$this->instanceName = $instance;
		$this->dbModule = $db_module;
		$this->permissions = $permissions;
	}
	
	/**
	 * Adds the necessary table for UserModule
	 *
	 * @return void
	 */
	public function installTables() {
		// No own table needed, uses Users
		
		// Add global permissions
		$params_perm = array(	'table' => 'Permissions',
        						'set' => array(	'module'=>$this->moduleName,
											'permissions'=>"users=@admin\nedit=@admin\nadd=@admin\npermissions=@admin"));
        if(!$this->dbModule->insertRow($params_perm)) {
			throw new Exception('Could not create global permissions for '.$this->moduleName.'.');
		}
	}
	
	
	/**
	 * This module has no public view, so it just returns an empty string
	 *
	 * @return string		empty string
	 */
	public function getView() {
		return '';
	}
	
	/**
	 * Returns the selected tab of the admin view
	 *
	 * @param string $tab 		selected tab
	 * @return string			html for selected tab
	 */
	protected function getAdminTab($tab="view") {
		switch($tab) {
			case "edit":
				return $this->getEditForm();
			case "add":
				return $this->getAddUserForm();
			case "permissions":
				return $this->getPermissionsForm();
			default:
				return $this->getList();
		}
	}
	
	/**
	 * Returns a list of users
	 *
	 * @return string			list of users in html form
	 */
	private function getList() {
		if($this->permissions['users'] != true) { return ''; }
		
		// Get the users
		$users = $this->findUserRows();
				
		$list = '<table>';
		$list .= '<tr><th class="citru_half_width">Username</th><th class="citru_half_width">Groups</th><th>Edit</th>';
		foreach ($users as $i=>$user) {
			if($i % 2 != 0) { $odd = ' class="citru_odd_element"'; } else { $odd = ''; }
			$link = $this->generateTablink('edit', 'user_id='.$user['id']);
			$list .= '<tr'.$odd.'><td>'.$user['name'].'</td><td>'.$user['groups'].'</td><td>'.$link.'</td></tr>';
		}
		$list .= '</table>';
		
		return $list;
	}
	
	/**
	 * Returns a form for editing settings
	 *
	 * @param string $message 		a message to display at the end of the form
	 * @return string				html for the edit form
	 */
	private function getEditForm($message="") {
		if($this->permissions['edit'] != true) { return ''; }
		
		// Check if settings should be saved first.
		if($_POST['instance'] == "UserModule_$this->instanceName" && $_POST['action'] == "edit") {
			$message = $this->saveFormData();
		}
		
		$users = $this->findUserRows();
		$user_names = array();
		$user_ids = array();
		$user_data = $users[0];
		if(preg_match("/[0-9]+/",$_GET['user_id'])) { $selected_user = $_GET['user_id']; }
		foreach ($users as $i=>$user) {
			$user_names[] = $user['id'].'. '.$user['name'];
			$user_ids[] = $user['id'];
			
			if($user['id'] == $selected_user) {
				$user_data = $user;
				$selected_number = $i;
			}
		}
		
		if($user_data['name'] == $_SESSION['user']) {
			$rm_dis = 'disabled="disabled"';
			$rm_dis_class = 'citru_disabled';
		}
		$select_user_elements = array(array('form_start', 'method'=>'get'),
								array('hidden_custom', 'name'=>$this->moduleName.'_'.$this->instanceName, 'value'=>'edit'),
								array('fieldset_start', 'legend'=>'Change user'),
								array('label', 'for'=>'user_id', 'text'=>'Username: ', 'class'=>'citru_left citru_right_space'),
								array('input_select', 'name'=>'user_id', 'options'=>$user_names, 'values'=>$user_ids, 'selected'=>$selected_number, 'class'=>'citru_left'),
								array('submit', 'value'=>'Change', 'icon'=>'refresh'),
								array('fieldset_end'),
								array('form_end'));
		$select_user = $this->generateForm($select_user_elements);
		
		$edit_user_elements = 	array(array('form_start'),
								array('hidden_instance'),
								array('hidden_action', 'value'=>'edit'),
								array('hidden_custom', 'name'=>'user_id', 'value'=>$user_data['id']),
								array('fieldset_start', 'legend'=>'User profile'),
								array('label', 'for'=>'groups', 'text'=>'Groups this user belongs to (@group1, @group2): ', 'after'=>'<br />'),
								array('input_text', 'name'=>'groups', 'value'=>$user_data['groups']),
								array('label', 'for'=>'pass', 'text'=>'New password: ', 'after'=>'<br />'),
								array('input_password', 'name'=>'pass', 'value'=>''),
								array('fieldset_end'),
								array('fieldset_start', 'legend'=>'Rename or remove'),
								array('label', 'for'=>'rename', 'text'=>'New name for user:'),
								array('input_text', 'name'=>'rename', 'value'=>$user_data['name']),
								array('input_checkbox', 'class'=>$rm_dis_class, 'name'=>'remove', 'text'=>'Remove the user '.$user_data['name'].'.', 'checked'=>$rm_dis),
								array('input_checkbox', 'class'=>$rm_dis_class, 'name'=>'remove_confirm', 'text'=>'Yes, I\'m sure!', 'checked'=>$rm_dis),
								array('fieldset_end'),
								array('footer_start'),
								array('custom', 'data'=>$message),
								array('submit', 'value'=>'Save', 'icon'=>'save'),
								array('footer_end'),
								array('form_end'));
		$edit_user = $this->generateForm($edit_user_elements);
		
		return $select_user.$edit_user;
	}
	
	/**
	 * Returns a form for editing settings
	 *
	 * @param string $message 			a message to display at the end of the form
	 * @return string					html for the form
	 */
	private function getAddUserForm($message="") {
		if($this->permissions['add'] != true) { return ''; }
		
		// Check if settings should be saved first.
		if($_POST['instance'] == "UserModule_$this->instanceName" && $_POST['action'] == "add") {
			$message = $this->addNewUser();
		}
		
		$add_user_elements = 	array(array('form_start'),
								array('hidden_instance'),
								array('hidden_action', 'value'=>'add'),
								array('fieldset_start', 'legend'=>'Add user'),
								array('label', 'for'=>'name', 'text'=>'User name', 'after'=>'<br />'),
								array('input_text', 'name'=>'name'),	
								array('label', 'for'=>'pass', 'text'=>'Password: ', 'after'=>'<br />'),
								array('input_password', 'name'=>'pass', 'value'=>''),
								array('label', 'for'=>'groups', 'text'=>'Groups this user belongs to (@group1, @group2): ', 'after'=>'<br />'),
								array('input_text', 'name'=>'groups'),
								array('fieldset_end'), 
								array('footer_start'),
								array('custom', 'data'=>$message),
								array('submit', 'value'=>'Add User', 'icon'=>'add'),
								array('footer_end'),
								array('form_end'));
		$add_user = $this->generateForm($add_user_elements);
		
		return $add_user;
	}
	
	/**
	 * Updates a previous instance from form data
	 *
	 * @return string			a message of how things went
	 */
	private function saveFormData() {
		$user_id = intval($_POST['user_id']);
		$groups = $_POST['groups'];	
		$pass = $_POST['pass'];	
		$remove = $_POST['remove'];
		$remove_confirm = $_POST['remove_confirm'];
		
		if($user_id <= 0) {
			return '<p class="citru_error">Invalid user id ('.$user_id.').</p>';
		}
		
		if(!preg_match("/^(\@[a-zA-Z]+[ ,]*)+$/",$groups)) {
			return '<p class="citru_error">Invalid groups format.</p>';
		}
		
		if($remove === 'on' and $remove_confirm === 'on') {
			$params = array(	"table" => "Users",
								"condition" => array(array( "id", "=", $user_id, "" )));
			$result = $this->dbModule->deleteRow($params);
			return '<p class="citru_event">Template removed.</p>';
		}
		
		// Check if the name should be changed
		$new_name = $_POST['rename'];
		if(strlen($new_name) < 4 || strlen($new_name) > 20) {
			return '<p class="citru_error">Username must be between 4 and 20 characters.</p>';
		}
		
		$set = array();
		$set['groups'] = $groups;
		$set['name'] = $new_name;
		
		if($pass != null) {
			if(strlen($pass) < 8 || strlen($pass) > 80) {
				return '<p class="citru_error">Password must be between 8 and 80 characters.</p>';
			}

			$pass_small = (int)(boolean)preg_match('/[a-z]/', $pass);
			$pass_big = (int)(boolean)preg_match('/[A-Z]/', $pass);
			$pass_nums = (int)(boolean)preg_match('/[0-9]/', $pass);
			$pass_other = (int)(boolean)preg_match('/[^A-Za-z0-9]/', $pass);

			if(($pass_small + $pass_big + $pass_nums + $pass_other) < 3) {
				return '<p class="citru_error">Password must contain at least one char from three of the following groups: lowercase ,uppercase , number and special char.</p>';
			}

			// Generate salt and hash
			$salt = sha1(uniqid(rand(), true));
			$pass_hash = sha1($pass);
			$hash = sha1($pass_hash.$salt);
			
			$set['salt'] = $salt;
			$set['hash'] = $hash;
			
		}
		$params = array(	"table" => "Users",
							"condition" =>	array(
											array( "id", "=", $user_id, "" )),
							"set" => 		$set );
																	
		$result = $this->dbModule->updateRow($params);
		
		return '<p class="citru_event">Form data saved.</p>';
	}
	
	/**
	 * Adds a new user based on form data
	 *
	 * @return string			a message of how things went
	 */
	private function addNewUser() {
		$name = $_POST['name'];
		$groups = $_POST['groups'];	
		$pass = $_POST['pass'];	

		if(strlen($name) < 4 || strlen($name) > 20) {
			return '<p class="citru_error">Username must be between 4 and 20 characters.</p>';
		}
		
		if(!preg_match("/^(\@[a-zA-Z]+[ ,]*)+$/",$groups)) {
			return '<p class="citru_error">Invalid groups format.</p>';
		}

		$set = array();
		$set['name'] = $name;
		$set['groups'] = $groups;
		
		if($pass != null) {
			if(strlen($pass) < 8 || strlen($pass) > 80) {
				return '<p class="citru_error">Password must be between 8 and 80 characters.</p>';
			}

			$pass_small = (int)(boolean)preg_match('/[a-z]/', $pass);
			$pass_big = (int)(boolean)preg_match('/[A-Z]/', $pass);
			$pass_nums = (int)(boolean)preg_match('/[0-9]/', $pass);
			$pass_other = (int)(boolean)preg_match('/[^A-Za-z0-9]/', $pass);

			if(($pass_small + $pass_big + $pass_nums + $pass_other) < 3) {
				return '<p class="citru_error">Password must contain at least one char from three of the following groups: lowercase ,uppercase , number and special char.</p>';
			}

			// Generate salt and hash
			$salt = sha1(uniqid(rand(), true));
			$pass_hash = sha1($pass);
			$hash = sha1($pass_hash.$salt);
			
			$set['salt'] = $salt;
			$set['hash'] = $hash;
			
		}
		$params = array(	"table" => "Users",
							"set" =>   $set );
																	
		$result = $this->dbModule->insertRow($params);
		
		return '<p class="citru_event">The user has been added.</p>';
	}
	
	/**
	 * Returns all the users
	 *
	 * @return array 			an array of user-rows (which also are arrays of values for id, name and groups)
	 */
	private function findUserRows() {
		$params = array(	"table" => "Users",
							"columns" => "id, name, groups",
							"condition" =>	array(
											array( "id", ">", "0", "" )),
							"order" => 'ID ASC');
		
		$users = $this->dbModule->findAll($params);
		return $users;
	}
	
	/**
	 * Returns a specific user
	 *
	 * @param integer $user_id 		user id to find
	 * @return array 				array of user data
	 */
	private function findUserRow($user_id) {
		$id = intval($user_id);
		$params = array(	"table" => "Users",
							"columns" => "id, name, groups",
							"condition" =>	array(
											array( "id", "=", "0", $id )),
							"order" => 'ID ASC');
		
		$user = $this->dbModule->findFirst($params);
		return $user;
	}
	
}

?>
